home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Languages / Mops 2.7 / Mops source / Toolbox classes / Scroller < prev    next >
Encoding:
Text File  |  1995-11-26  |  14.9 KB  |  508 lines  |  [TEXT/MSET]

  1. \ Scroller - view which supports scroll bars.
  2.  
  3. \ May 91    mrh    Added horizontal scroll bar support.
  4. \ Oct 91    mrh    Changed owner from Window to View.  Replaces vscroll 
  5. \ May 92    mrh    Changed to "new-style" control.
  6. \ June 92    mrh    Fixed GetRect: in Scroller.
  7. \ Feb 93    mrh Introduced class BigRect for PanRects, to allow humungous rects.
  8. \ Sept 93    mrh    Revised for new control scheme - controls now a view subclass.
  9. \ Nov95        JRF    now properly hiding and showing scrollbars
  10.  
  11. need    view
  12. need    ctl
  13.  
  14.  
  15. \ SCROLLER is a view which has support for a vertical and horizontal
  16. \ scroll bar along the right hand and bottom edge respectively.  We implement
  17. \ it with three child views: mainView, which is the display area, and the
  18. \ two scroll bars themselves.
  19.  
  20. \ MainView is an instance of a one-off class, Mview.  This class has a
  21. \ rectangle, PanRect, which normally ought to enclose all the child views
  22. \ of the Mview.  The usual scenario is that PanRect is larger than the viewRect,
  23. \ and scrolling amounts to shifting the child views (and PanRect) around within
  24. \ the viewRect - which, from another point of view, can be thought of as
  25. \ "panning" the viewRect over the PanRect area.
  26.  
  27. \ Mview has appropriate methods for returning the distances by which PanRect
  28. \ falls outside the viewRect area, so that the parent Scroller can set the
  29. \ scroll bar values appropriately.
  30.  
  31. \ One unusual thing we do here is to override addView: on Scroller so that it
  32. \ becomes an ADDVIEW: on MainView, since this is usually what we really mean.
  33. \ In the case where you want to really addView: on the Scroller, such as to add
  34. \ another child view alongside one of the scroll bars, you should subclass
  35. \ Scroller with the extra views as ivars, and at run time do addView: super
  36. \ as we do for the scroll bars (see the NEW: method).
  37.  
  38. \ Another approach we could have taken to implementing MainView would have been
  39. \ as a pointer, with late binding.  That way MainView could have been any
  40. \ view subclass.  That would have been more flexible, but possibly overkill
  41. \ for what we usually want to do - it would have required a more complex
  42. \ setting-up process, with the MainView address having to be passed in after
  43. \ NEW: has been done.  But if you need the extra flexibility, feel free to clone
  44. \ Scroller and make the changes!
  45.  
  46. \ PanRect can obviously be very big, so we don't implement it as a regular rect,
  47. \ but define a new class, BigRect, which uses vars rather than ints for the
  48. \ coordinates.
  49.  
  50.  
  51.  
  52. 0    value    ClickedScroller
  53.                 \ CLICK: on a Scroller puts the Scroller's addr here, so
  54.                 \ child views can easily send messages back to the clicked
  55.                 \ Scroller.  Scroll bars use this, also TextEdit views.
  56.                 \ I could have just used ThisCtl, but if another control
  57.                 \ is involved somewhere it might get clobbered.  Unlikely,
  58.                 \ but I'm a cautious individual.
  59.  
  60. : 1R    1Right:        [ clickedScroller ]  ;
  61. : 1L    1Left:        [ clickedScroller ]  ;
  62. : 1U    1Up:        [ clickedScroller ]  ;
  63. : 1D    1Down:        [ clickedScroller ]  ;
  64.  
  65. : PGR    pgRight:    [ clickedScroller ]  ;
  66. : PGL    pgLeft:        [ clickedScroller ]  ;
  67. : PGU    pgUp:        [ clickedScroller ]  ;
  68. : PGD    pgDown:        [ clickedScroller ]  ;
  69.  
  70. : VD    Vdrag:        [ clickedScroller ]  ;
  71. : HD    Hdrag:        [ clickedScroller ]  ;
  72.  
  73.  
  74.  
  75.  
  76. \            ================= BigRect ===================
  77.  
  78. \ BIGRECT is exactly that -- using vars rather than ints for the
  79. \ coordinates.  The toolbox doesn't support this, so we just use
  80. \ it in places where we need very big rectangles and control
  81. \ everything ourselves.  So far we only need to support GET:, PUT:
  82. \ and SHIFT: methods.
  83.  
  84. :class  BIGRECT  super{ object }
  85. record
  86. {    var        TOP
  87.     var        LEFT
  88.     var        BOTTOM
  89.     var        RIGHT
  90. }
  91.  
  92. :m GET:        get: left  get: top  get: right  get: bottom  ;m
  93. :m PUT:        put: bottom  put: right  put: top  put: left  ;m
  94.  
  95. :m SHIFT:  { dx dy -- }
  96.     dx dy or  0EXIT
  97.     dx +: left  dx +: right
  98.     dy +: top   dy +: bottom  ;m
  99.     
  100. :m INSET:  { dx dy -- }
  101.     dx +: left  dx -: right
  102.     dy +: top   dy -: bottom  ;m
  103.  
  104. ;class
  105.  
  106.  
  107. \            ================= Mview ===================
  108.  
  109. \ MVIEW is a view which we use for the main view of a Scroller (the view
  110. \ with the actual contents - the other two views are the two scroll
  111. \ bars).  It has methods to shift its children, or, depending on
  112. \ the point of view, "panning" over the children.
  113.  
  114.  
  115. :class  MVIEW  super{ view }
  116. record
  117. {    bigrect        PANRECT        \ Rect for "panning" children.  Ought to
  118.                             \  contain all of them.  Can be enormous.
  119. }
  120.  
  121. :m GETPANRECT:
  122.     get: panRect  ;m
  123.     
  124. :m PUTPANRECT:
  125.     put: panRect  ;m
  126.  
  127.  
  128. \ SHIFTCHILDREN ( dx dy -- )  moves all the child views by
  129. \ the given distance.  We do this by changing their bounds appropriately
  130. \ then calling MOVED:.
  131.  
  132. :m SHIFTCHILDREN:  { dx dy \ theChild l t r b -- }
  133.     BEGIN  each: children
  134.     WHILE
  135.         -> theChild
  136.         theChild getBounds: view  -> b  -> r  -> t  -> l
  137.         dx ++> l  dx ++> r
  138.         dy ++> t  dy ++> b
  139.         l t r b  theChild  setBounds: view
  140.         moved: [ theChild ]        \ late bind here as different things may happen
  141.     REPEAT  ;m
  142.  
  143. private
  144.     
  145. :m HowFar:  { offs1 offs2 -- offs' }
  146.     offs1 offs2 xor 0>                    \ Same sign?
  147.     IF        offs1  offs2 dup 0<
  148.             IF  max  else  min  THEN
  149.     ELSE    0
  150.     THEN  ;m
  151.  
  152. \ CoercePanRect: shifts the children so that panRect falls as far
  153. \ within the viewRect as possible.  We factor out (CoercePanRect):
  154. \ which does the basic stuff that Scroller subclasses can use.
  155.  
  156. public
  157.  
  158. :m (CoercePanRect):  { \ pLeft pTop pRt pBot dx dy -- dx dy  }
  159.     \ Returns the amount we have to shift panRect to get it into
  160.     \ the right position.  We pass in panRect's coordinates so that
  161.     \ Scroller subclasses can use a different panRect (TEScroller
  162.     \ does this).
  163.     
  164.     get: panRect  -> pBot -> pRt -> pTop -> pLeft
  165.     getTopX: viewRect  pLeft -
  166.     getBotX: viewRect  pRt   -   howFar: self  -> dx
  167.     getTopY: viewRect  pTop  -
  168.     getBotY: viewRect  pBot  -   howFar: self  -> dy
  169.     dx dy shift: panRect
  170.     dx  dy  ;m
  171.     
  172.  
  173. :m CoercePanRect:  { \ dx dy -- }
  174.     (coercePanRect): self  -> dy  -> dx
  175.     dx dy or  0EXIT
  176.     dx dy  shiftChildren: self  ;m
  177.  
  178.  
  179. \ Here we define the default panRect to be the rect which just contains
  180. \ all the child views.  Change as necessary.
  181.  
  182. :m DfltPanRect:  { \ left top rt bot -- }
  183.     first?: children
  184.     NIF        0 -> bot  0 -> rt  0 -> top  0 -> left
  185.     ELSE    getRect: []  -> bot  -> rt  -> top  -> left
  186.     THEN
  187.     BEGIN    each: children
  188.     WHILE    getRect: []
  189.         bot max -> bot    rt max -> rt
  190.         top min -> top  left min -> left
  191.     REPEAT
  192.     left top rt bot  put: panRect  ;m
  193.  
  194. ;class
  195.  
  196.  
  197.  
  198. \            ================= Scroller ===================
  199.  
  200. \ SCROLLER is a view which has support for a vertical and horizontal
  201. \ scroll bar along the right hand and bottom edge respectively.
  202. \ Either may be present or absent, and may have an offset or gap
  203. \ at either end of a specified amount.
  204.  
  205. :class    SCROLLER  super{ view }
  206.  
  207.     mview    MainView        \ The display area, minus the scroll bars
  208.     vscroll    TheVscroll
  209.     hscroll    TheHscroll
  210.  
  211. record
  212. {    bool    vscroll?        \ True if v scroll bar to be used
  213.     bool    hscroll?        \ True if h scroll bar to be used
  214.     bool    UsePanRect?        \ True if we're to use PanRect
  215.  
  216.     var        HPAN            \ Horizontal panning range
  217.     var        HPOS            \ Current vertical posn
  218.     var        VPAN            \ Vertical ditto
  219.     var        VPOS
  220.  
  221.     int        HUNIT            \ # pixels for one horizontal arrow click
  222.     int        VUNIT
  223. }
  224.  
  225. :m SetPanRanges:  { \ left top rt bot pLeft pTop pRt pBot -- }
  226.     noClip
  227.     getViewRect: mainView  -> bot  -> rt  -> top  -> left
  228.     getPanRect: mainView  -> pBot  -> pRt  -> pTop  -> pLeft
  229.     left pLeft -  dup  0 max  put: Hpos
  230.     pRt rt -  +  0  max  put: Hpan
  231.     top pTop -  dup  0 max  put: Vpos
  232.     pBot bot -  +  0 max  put: Vpan
  233.     get: vscroll?
  234.     IF    0  get: vpan  putRange: theVscroll
  235.         get: vpan
  236.         IF    get: vpos  put: theVscroll
  237.             enable: theVscroll
  238.         ELSE
  239.             0 put: theVscroll
  240.             disable: theVscroll
  241.         THEN
  242.     THEN
  243.     get: hscroll?
  244.     IF    0  get: hpan  putRange: theHscroll
  245.         get: hpan
  246.         IF    get: hpos  put: theHscroll
  247.             enable: theHscroll
  248.         ELSE
  249.             0 put: theHscroll
  250.             disable: theHscroll
  251.         THEN
  252.     THEN  ;m
  253.  
  254.  
  255. :m FixPanRect:
  256.     get: usePanRect?  NIF  dfltPanRect: mainView  THEN
  257.     coercePanRect: mainView
  258.     setPanRanges: self  ;m
  259.  
  260.  
  261. :m FixMainViewBounds:
  262.     getBounds: mainView  2drop        \ Don't change left or top
  263.     -16 get: vscroll? and  -16 get: hscroll? and
  264.     setBounds: mainView  ;m
  265.  
  266.  
  267. :m FixHscrollBounds:
  268.     -1 -16  -15 0    \ ****get: vscroll? and  0 Nov95 JRF
  269.             \ JRF moved left 1 pixel to left
  270.     setBounds: theHscroll  moved: theHscroll  ;m
  271.  
  272. :m FixVscrollBounds:
  273.     -16  -1  0  -15        \ ****get: hscroll? and
  274.                         \ JRF moved top up 1 pixel
  275.     setBounds: theVscroll  moved: theVscroll  ;m
  276.  
  277. public
  278.  
  279.  
  280. ( b -- )
  281. :m VSCROLL:     put: vscroll?  fixMainViewBounds: self  ;m
  282. :m HSCROLL:     put: hscroll?  fixMainViewBounds: self  ;m
  283.  
  284.  
  285. :m PUTPANRECT:  ( l t r b -- )
  286.     putPanRect: mainView  true put: usePanRect?
  287.     coercePanRect: mainView  setPanRanges: self  ;m
  288.  
  289. :m ADDVIEW:        addView: mainView  ;m
  290.  
  291. ( n -- )
  292. :m >HUNIT:    put: Hunit  ;m
  293. :m >VUNIT:    put: Vunit  ;m
  294.  
  295. :m >VRANGE:    putRange: theVscroll  ;m
  296. :m >HRANGE:    putRange: theHscroll  ;m
  297.  
  298. :m ?VENABLE:
  299.     get: vscroll?  0EXIT
  300.     show: theVscroll    \ Nov95 JRF now properly hiding and showing scrollbars
  301.     get: Vpan  0EXIT
  302.     enable: theVscroll  ;m
  303.  
  304. :m ?HENABLE:
  305.     get: hscroll?  0EXIT
  306.     show: theHscroll    \ Nov95 JRF
  307.     get: Hpan  0EXIT
  308.     enable: theHscroll  ;m
  309.  
  310.  
  311. :m NEW:        \ mainView and the 2 scroll bars are ivars, but they have to be
  312.             \  children as well!
  313.     addr: mainView        addView: super
  314.     get: hscroll?  IF  addr: theHscroll  addView: super  THEN
  315.     get: vscroll?  IF  addr: theVscroll  addView: super  THEN
  316.     new: super
  317.     fixHscrollBounds: self  fixVscrollBounds: self
  318.     fixPanRect: self  ;m
  319.  
  320.  
  321. :m ENABLE:
  322.     get: alive?  0EXIT
  323.     ?Venable: self  ?Henable: self  enable: super  ;m
  324.  
  325. :m DISABLE:
  326.     get: alive?  0EXIT
  327.     get: vscroll?  if  disable: theVscroll  hide: theVscroll then    \ JRF
  328.     get: hscroll?  if  disable: theHscroll  hide: theHscroll then    \ JRF
  329.     disable: super  ;m
  330.  
  331.  
  332. :m MOVED:
  333.     moved: super
  334.     fixPanRect: self
  335.     update: self  ;m
  336.  
  337.  
  338.  
  339. \ PAN: ( dx dy -- )  pans the view over the subviews by the given distance.
  340. \ Doesn't alter the scroll bars -- use PANRIGHT: etc. for this, since they
  341. \ adjust the appropriate scroll bar and then call PAN:.
  342.  
  343. \ Our convention is that positive dx and dy correspond to a pan to the
  344. \ right and down, which means that the subviews are being shifted to the
  345. \ left and up, which is a "negative" shift.  It's very easy to get this
  346. \ mixed up, but it would be just as confusing if I did it the other way
  347. \ around.  If something doesn't work, try reversing the signs!!
  348.  
  349. \ Another point to note is that I've found by experimentation that if
  350. \ the mouse is held down in a scroll bar arrow, our arrow routine, which
  351. \ is passed to TrackControl as a proc, gets called continually -- thus we
  352. \ can't handle an update event on the window are until mouse-up.  I'm not
  353. \ even sure there is an update event until then, anyway.
  354. \ I guess Apple's idea is that each time the origin should get
  355. \ shifted, so that the little rectangles which are invalidated each time
  356. \ get accumulated properly.  But in our way of doing things, we're using
  357. \ the grafport origin all the time (until a DRAW: is done), so the same
  358. \ rectangle would get invalidated repeatedly.  So we handle this with an
  359. \ ivar, #updates.  If we get a PAN: call and #updates is zero, we call
  360. \ InvalRect as normal.  If #updates is 1, the little rectangle will already
  361. \ be invalid, but rather than trying to invalidate an adjacent rectangle
  362. \ we take the easy way out and invalidate the whole viewRect.  At least
  363. \ that way we can be sure we don't miss updating something.  If #updates
  364. \ is greater than 2, we've already invalidated the viewRect, so there's
  365. \ nothing left to do -- so that's exactly what we do.
  366.  
  367. \ Another point that has come out through experimentation is that the
  368. \ scroll bar which has had its arrow clicked must not be clipped out, or
  369. \ the thumb isn't redrawn in the right position.  The redraw is done by
  370. \ the system, but mustn't be clipped out.  So we set the clip to the right
  371. \ contents area with ClipRect, scroll the rectangle, then at the end set
  372. \ the clip to the rect containing the appropriate scroll bar so that the
  373. \ system will redraw it properly. 
  374.  
  375. :m PAN:  { dx dy \ #upd hext vext -- }
  376.     dx  +: hpos  dy +: vpos
  377.     neg> dx  neg> dy
  378.     ^viewRect: mainView  dup  call ClipRect
  379.     dx dy pack  theRgn  call ScrollRect
  380.     get: #updates  -> #upd  #upd 1+ 100 min  put: #updates
  381.     #upd
  382.     NIF        theRgn  call InvalRgn  false put: setClip?
  383.     ELSE    #upd 1 = IF  ^viewRect: mainView  call InvalRect  THEN
  384.     THEN
  385.     dx dy  shiftChildren: mainView
  386.     noClip  ;m
  387.     
  388. \ Note: it turns out we need the noClip so that the scroll bar arrow
  389. \ always unhilites.
  390.     
  391.  
  392. :m PANRIGHT:  { dx \ hs -- }
  393.     get: theHscroll  -> hs
  394.     hs dx +  get: Hpan  >
  395.     IF  get: Hpan  hs -  -> dx  THEN
  396.     dx  0EXIT
  397.     hs dx +  put: theHscroll
  398.     dx  0  pan: self  ;m
  399.  
  400. :m PANLEFT:  { dx \ hs -- }
  401.     get: theHscroll  -> hs   hs 0EXIT
  402.     hs dx -  0< if  hs -> dx  then
  403.     hs dx -  put: theHscroll
  404.     dx negate  0  pan: self  ;m
  405.  
  406. :m PANDOWN:  { dy \ vs -- }
  407.     get: theVscroll  -> vs
  408.     vs dy +  get: Vpan  >
  409.     IF  get: Vpan  vs -  -> dy  THEN
  410.     dy  0EXIT
  411.     vs dy +  put: theVscroll
  412.     0  dy  pan: self  ;m
  413.  
  414. :m PANUP:  { dy \ vs -- }
  415.     get: theVscroll  -> vs   vs 0EXIT
  416.     vs dy - 0<  IF  vs -> dy  THEN
  417.     vs dy -  put: theVscroll
  418.     0  dy negate  pan: self  ;m
  419.  
  420.  
  421. :m HPAGE:  { \ left top rt bot -- #pixels }
  422.     get: viewRect  -> bot  -> rt  -> top  -> left
  423.     rt left -  get: Hunit -  0 max  ;m
  424.  
  425. :m VPAGE:  { \ left top rt bot -- #pixels }
  426.     get: viewRect  -> bot  -> rt  -> top  -> left
  427.     bot top -  get: Vunit -  0 max  ;m
  428.  
  429. :m 1RIGHT:    get: Hunit  panRight: self  ;m
  430. :m 1LEFT:    get: Hunit  panLeft: self   ;m
  431. :m 1UP:        get: Vunit  panUp: self     ;m
  432. :m 1DOWN:    get: Vunit  panDown: self   ;m
  433.  
  434. :m PGRIGHT:    hPage: self  panRight: self    ;m
  435. :m PGLEFT:    hPage: self  panLeft: self    ;m
  436. :m PGUP:    vPage: self  panUp: self    ;m
  437. :m PGDOWN:    vPage: self  panDown: self    ;m
  438.  
  439. :m VDRAG:    0  get: theVscroll  get: vpos -  pan: self  ;m
  440. :m HDRAG:    get: theHscroll  get: hpos -  0  pan: self  ;m
  441.     
  442.  
  443. \ The CLICK: method only has to do one extra thing over what View
  444. \ provides - we put the addr of this Scroller in clickedScroller so the
  445. \ scroll bar action handlers can send messages back to us.
  446.  
  447. :m CLICK:
  448.     ^base -> clickedScroller  click: super  ;m
  449.  
  450. :m CLASSINIT:
  451.     classinit: super
  452.     true  vscroll: self   true  hscroll: self        \ Defaults
  453.     4 dup  put: Hunit  put: Vunit
  454.     XTS{ 1l 1r pgl pgr hd }  actions: theHscroll
  455.     XTS{ 1u 1d pgu pgd vd }  actions: theVscroll
  456.     parRight parTop parRight parBottom        setJust: theVscroll
  457.     parLeft parBottom parRight parBottom    setJust: theHscroll
  458.  
  459.     parLeft parTop parRight parBottom  setJust: mainView
  460. ;m
  461.  
  462. ;class
  463.  
  464.  
  465. endload
  466.  
  467.  
  468. \ Testing - this sets up a Scroller.
  469.  
  470. scroller    SS
  471. button        BB            \ A child view which is a button
  472.  
  473.  
  474. 40 40 300 200    setBounds: ss
  475.  
  476. 10 10  " Click here"    init: bb
  477.  
  478.  
  479. : Drawit    draw: tempRect  ;        \ A draw handler which just draws the viewRect
  480.  
  481. : DrawSS    draw: ss  ;                \ Draw handler for fWind for test
  482.  
  483. : Clicked
  484.     noclip
  485.     ." clicked " .id: [self] cr
  486. \ Now we expand ss a bit to check if the scroll bars move and resize:
  487.     getBounds: ss
  488.     10 +
  489.     swap 20 + swap
  490.     setBounds: ss  moved: ss  ;
  491.  
  492.  
  493. : contentClick            \ New content click handler for fWind
  494.             click: ss  drop  ;
  495.             
  496. ' drawit    setDraw: ss
  497.  
  498. ' clicked    dup setclick: ss  setclick: bb
  499.  
  500. : GO
  501.     cls
  502.     xts{ null null drawSS contentClick }  actions: fWind
  503.     bb addview: ss
  504.     fWind  setWindow: ss    \ Normally done automatically from NEW: in Window+
  505.     new: ss                    \ Ditto
  506.     0 0 1000 1000  putPanRect: ss
  507.     draw: ss  ;
  508.